#include "modbus_master.h" // 包含modbus主机模式头文件

/* ----------------- 配置区 ----------------- */
#define MB_RX_FRAME_MAX 50
#define MB_QUEUE_LEN 10
#define MB_TIMEOUT_MS 300

/* 状态机状态 */
// ----------------- Modbus RTU 状态机状态枚举 -----------------
typedef enum
{
    MB_WAIT_SLAVE = 0,
    /*
        等待从机地址
        - 报文第一个字节必须是合法从机地址 (1~247)
        - 作用：同步报文起始，避免干扰数据影响解析
        - 收到非法字节会直接忽略，状态机保持在此状态
    */

    MB_WAIT_FUNC,
    /*
        等待功能码字节
        - 报文第二个字节是功能码（Function Code）
        - 决定后续报文长度和结构：
            03/04 → 数据长度不固定，需要等待 ByteCount
            06/10 → 响应报文长度固定
        - 不支持功能码 → 丢弃报文，回到 MB_WAIT_SLAVE
    */

    MB_WAIT_LEN_OR_REG,
    /*
        等待长度或寄存器信息
        - 只对功能码 03/04 有效
        - 接收到 ByteCount 后计算期望总长度：
            expected_len = ByteCount + 5
            （5 = 从机地址 + 功能码 + ByteCount + 2字节CRC）
        - 切换到 MB_WAIT_DATA 接收实际数据
    */

    MB_WAIT_DATA,
    /*
        等待报文数据段
        - 接收报文中的数据部分，逐字节存入缓冲
        - 当接收字节数达到 expected_len - 2（除去 CRC）时
          → 切换到 MB_WAIT_CRC1 接收 CRC
        - 工业注意点：
            - 如果报文被中断或丢帧，需要配合超时机制重置状态机
    */

    MB_WAIT_CRC1,
    /*
        等待 CRC 低字节
        - Modbus RTU CRC16 的低 8 位
        - 接收到后存入报文缓冲，切换到 MB_WAIT_CRC2
        - 若报文中断，会在此状态等待字节，因此必须超时保护
    */

    MB_WAIT_CRC2
    /*
        等待 CRC 高字节
        - Modbus RTU CRC16 的高 8 位
        - 接收到完整 CRC 后：
            - 计算实际 CRC 与报文 CRC 对比
                - CRC 正确 → 将完整报文发送到队列供上层解析
                - CRC 错误 → 丢弃报文，记录错误日志
        - 状态机重置到 MB_WAIT_SLAVE，准备接收下一个报文
    */
} MB_State_t;

/* 队列项 */
typedef struct
{
    uint16_t len;
    uint8_t buf[MB_RX_FRAME_MAX];
} MBFrame_t;
static osMessageQueueId_t xMBFrameQueue = NULL; // modbus主机模式 状态机解析任务 消息队列句柄

/* ----------------- CRC ----------------- */
static uint16_t MB_CRC16(const uint8_t *data, uint16_t len)
{
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < len; i++)
    {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++)
        {
            if (crc & 1)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc >>= 1;
        }
    }
    return crc;
}

// ------------------ modbus主机模式 组件初始化任务 ------------------
void modbus_master_init_task(void *arg)
{
    (void)arg;
    //  初始化环形缓冲（动态方式）
    ringbuf_global_init();

    // 初始化UART0和UART1 确保串口可用
    uart_ctx_init(&uart_list[0], CM_UART_DEV_0, 9600);
    uart_ctx_init(&uart_list[1], CM_UART_DEV_1, 115200);

    if (xMBFrameQueue == NULL)
    {
        xMBFrameQueue = osMessageQueueNew(10, sizeof(MBFrame_t), NULL);
        cm_log_printf(0, "Modbus frame queue init %s!\n", (xMBFrameQueue != NULL) ? "done" : "failed");
    }
    else
    {
        cm_log_printf(0, "Modbus frame queue already exists!\n");
    }

    // ----------------- 创建状态机任务 -----------------
    osThreadAttr_t stateMachineTaskAttr = {
        .name = "MBStateTask",       // 任务名称
        .stack_size = 1024,          // 栈大小
        .priority = osPriorityNormal // 状态机优先级稍高
    };

    osThreadId_t stateMachineTaskHandle = osThreadNew((osThreadFunc_t)MB_StateMachineTask, NULL, &stateMachineTaskAttr);
    cm_log_printf(0, "MBStateTask init %s!\n", (stateMachineTaskHandle != NULL) ? "ok" : "failed");

    // ----------------- 创建轮询任务 -----------------
    osThreadAttr_t pollTaskAttr = {
        .name = "MBPollTask",        // 任务名称
        .stack_size = 1024,          // 栈大小（按需调整）
        .priority = osPriorityNormal // 轮询任务优先级稍低
    };

    osThreadId_t pollTaskHandle = osThreadNew((osThreadFunc_t)MB_PollTask, NULL, &pollTaskAttr);
    cm_log_printf(0, "MBPollTask init %s!\n", (pollTaskHandle != NULL) ? "ok" : "failed");
}

/* ----------------- 状态机解析任务 ----------------- */
void MB_StateMachineTask(void *arg)
{
    uint8_t byte = 0;                  // 存储从环形缓冲区读取的字节
    MB_State_t state = MB_WAIT_SLAVE;  // 初始状态等待从机地址
    MBFrame_t frame;                   // 缓存完整报文
    uint16_t idx = 0;                  // 当前报文缓冲索引
    uint16_t expected_len = 0;         // 当前报文期望长度
    uint8_t func = 0;                  // 功能码
    uint64_t lastReceiveTime = 0;      // 上次接收到字节的时间
    uint32_t crcErrorCount = 0;        // CRC 错误统计
    const uint32_t MAX_CRC_ERRORS = 5; // 连续CRC错误阈值

    // 初始化帧缓冲区（假设MBFrame_t的buf是uint8_t数组）
    memset(&frame, 0, sizeof(MBFrame_t));

    while (1)
    {
        // ----------------- 超时检测（关键优化：无论缓冲区是否为空，都检测超时） -----------------
        if (state != MB_WAIT_SLAVE)
        {
            uint64_t currentTime = cm_rtc_get_current_time();
            if (currentTime - lastReceiveTime > MB_TIMEOUT_MS)
            {
                cm_log_printf(0, "Modbus timeout, resetting state machine\n");
                goto reset_state_machine; // 跳转到通用重置逻辑
            }
        }

        // ----------------- 读取环形缓冲区数据（关键修复：实际读取数据） -----------------
        if (ringbuf_is_empty(uart0_ringbuf))
        {
            osDelay(1); // 无数据时等待，降低CPU占用
            continue;
        }

        ringbuf_pop(uart0_ringbuf, &byte); // 核心修复：读取数据到byte

        lastReceiveTime = cm_rtc_get_current_time(); // 更新接收时间戳

        // ----------------- 状态机解析 -----------------
        switch (state)
        {
        case MB_WAIT_SLAVE:
            // 等待从机地址（1~247为合法地址，0为广播地址，可根据需求处理）
            if (byte < 1 || byte > 247)
            {
                cm_log_printf(0, "Invalid slave address: %d\n", byte);
                break;
            }
            frame.buf[0] = byte;
            idx = 1;
            state = MB_WAIT_FUNC;
            break;

        case MB_WAIT_FUNC:
            // 等待功能码
            frame.buf[idx++] = byte;
            func = byte;

            // 判断功能码并设置期望长度（关键修复：区分请求/响应帧）
            switch (func)
            {
            case 0x03: // 读取保持寄存器（请求/响应）
            case 0x04: // 读取输入寄存器（请求/响应）
                // 主机请求帧：从机地址(1) + 功能码(1) + 寄存器地址(2) + 寄存器数量(2) + CRC(2) → 总长度8
                // 从机响应帧：从机地址(1) + 功能码(1) + ByteCount(1) + 数据(n) + CRC(2) → 长度=5 + n（n=寄存器数量×2）
                // 此处先假设是请求帧，后续接收数据时再动态调整（或根据实际角色优化）
                expected_len = 8; // 先按请求帧长度初始化
                state = MB_WAIT_DATA;
                break;

            case 0x06: // 写单个寄存器（请求/响应长度均为8）
                expected_len = 8;
                state = MB_WAIT_DATA;
                break;

            case 0x10: // 写多个寄存器（请求：1+1+2+2+1+n+2= n+9；响应：1+1+2+2+2=8）
                // 简化处理：假设此处只处理响应帧（根据实际需求调整）
                expected_len = 8;
                state = MB_WAIT_DATA;
                break;

            default:
                cm_log_printf(0, "Unsupported Modbus function: %02X\n", func);
                goto reset_state_machine;
            }
            break;

        case MB_WAIT_DATA:
            // 接收数据部分（包含地址、数量、ByteCount等）
            frame.buf[idx++] = byte;

            // 关键优化：动态调整期望长度（针对响应帧）
            if (func == 0x03 || func == 0x04)
            {
                // 若当前是响应帧，且已接收ByteCount字段（idx=3时，buf[2]是ByteCount）
                if (idx == 3)
                {
                    expected_len = 5 + frame.buf[2]; // 5=1(地址)+1(功能码)+1(ByteCount)+2(CRC)
                }
            }

            // 数据接收完成（达到期望长度-2，等待CRC）
            if (idx >= expected_len - 2)
            {
                state = MB_WAIT_CRC1;
            }
            break;

        case MB_WAIT_CRC1:
            // 接收CRC低字节
            frame.buf[idx++] = byte;
            state = MB_WAIT_CRC2;
            break;

        case MB_WAIT_CRC2:
            // 接收CRC高字节
            frame.buf[idx++] = byte;

            // CRC校验
            uint16_t crcRecv = (frame.buf[idx - 1] << 8) | frame.buf[idx - 2]; // 注意字节序：高字节在前
            uint16_t crcCalc = MB_CRC16(frame.buf, idx - 2);

            if (crcRecv == crcCalc)
            {
                frame.len = idx;
                // 发送到消息队列
                if (osMessageQueuePut(xMBFrameQueue, &frame, 0, 0) != osOK)
                {
                    cm_log_printf(0, "Warning: Modbus queue full, frame dropped\n");
                }
                crcErrorCount = 0; // 重置错误计数
            }
            else
            {
                crcErrorCount++;
                cm_log_printf(0, "Modbus CRC error #%lu, slave=%d func=%02X\n", crcErrorCount, frame.buf[0], func);
                // 连续错误超过阈值，打印警告
                if (crcErrorCount >= MAX_CRC_ERRORS)
                {
                    cm_log_printf(0, "Warning: Too many CRC errors, check communication\n");
                }
            }

            // 重置状态机
        reset_state_machine:
            idx = 0;
            state = MB_WAIT_SLAVE;
            memset(frame.buf, 0, idx); // 清空缓冲区（可选，避免脏数据）
            break;

        default:
            cm_log_printf(0, "Unknown Modbus state, resetting\n");
            goto reset_state_machine;
        }
    }
}

// 轮询任务
void MB_PollTask(void *arg)
{
    unsigned long lastWakeTick = 0;

    MBFrame_t frame;   // 用于存储从队列获取的Modbus响应帧
    uint8_t index = 0; // 当前轮询索引

    // 指向当前活跃的轮询表和长度
    const ModbusPollItem_t *current_poll_table = NULL;
    uint8_t current_poll_count = 0;
    FactoryCODE last_factory_code = (FactoryCODE)-1; // 上次轮询的品牌代码
    uint8_t is_round_complete = 0;                   // 新增：标记是否完成一轮轮询
    while (1)
    {

        // 检查厂家代码是否发生变化，如果变化则切换轮询表
        if (gInverterData.factory_code != last_factory_code)
        {
            last_factory_code = gInverterData.factory_code;

            // 根据厂家代码查找对应的配置
            if (last_factory_code < FactoryCODE_MAX)
            {
                current_poll_table = factory_config_map[last_factory_code].poll_table;
                current_poll_count = factory_config_map[last_factory_code].poll_count;
                index = 0; // 切换配置后，从第一个条目开始轮询

                if (current_poll_table)
                {
                    cm_log_printf(0, "Switched to poll table for factory: %d\n", last_factory_code);
                }
                else
                {
                    cm_log_printf(0, "No poll table defined for factory: %d\n", last_factory_code);
                }
            }
            else
            {
                current_poll_table = NULL;
                current_poll_count = 0;
                cm_log_printf(0, "Invalid factory code: %d\n", last_factory_code);
            }
        }

        // 如果没有有效的轮询表，则等待
        if (current_poll_table == NULL || current_poll_count == 0)
        {
            osDelay(1000);
            continue;
        }

        // 获取当前要轮询的项目
        const ModbusPollItem_t *item = &current_poll_table[index];
        uint8_t retry = item->retry;

        cm_log_printf(0, "MB_PollTask: Polling index=%d for factory %d\n", index, last_factory_code);

        while (retry--)
        {

            MB_SendRequest(gInverterData.adder, item->func, item->reg, item->num, item->data);

            /* 等待队列响应 */
            if (osMessageQueueGet(xMBFrameQueue, &frame, NULL, 300 / portTICK_PERIOD_MS) == osOK)
            {
                if (item->parse)
                {
                    modbus_log_data(MB_DATA_RECEIVE, frame.len, frame.buf); // 打印接收数据
                    item->parse(frame.buf, frame.len);
                }
                break;
            }
            else
            {
                // 打印日志cm_log_printf
                cm_log_printf(0, "Timeout slave=%d func=%02X reg=%04X num=%04X\n", gInverterData.adder, item->func, item->reg, item->num);
            }
        }

        // 关键修改：轮询索引更新与一轮完成判断
        index = (index + 1) % current_poll_count;
        if (index == 0 && !is_round_complete)
        { // 刚完成一轮且未触发过延迟
            cm_log_printf(0, "Complete one poll round, delay 5s\n");
            osDelay_absolute_safe(&lastWakeTick, 5000); // 一轮结束后延迟10s
            is_round_complete = 1;                      // 标记已触发延迟
        }
        else
        {
            is_round_complete = 0; // 未完成一轮，重置标志位
            osDelay_absolute_safe(&lastWakeTick, item->interval);
        }
    }
}

/* ----------------- Modbus 发送封装 ----------------- */
void MB_SendRequest(uint8_t slave, uint8_t func, uint16_t reg, uint16_t num, const uint8_t *data)
{
    uint8_t buf[MB_RX_FRAME_MAX];
    uint16_t len = 0;

    buf[len++] = slave;
    buf[len++] = func;

    switch (func)
    {
    case 0x03: // 读保持寄存器
    case 0x04: // 读输入寄存器
        buf[len++] = reg >> 8;
        buf[len++] = reg & 0xFF;
        buf[len++] = num >> 8;
        buf[len++] = num & 0xFF;
        break;
    case 0x06: // 写单个寄存器
        buf[len++] = reg >> 8;
        buf[len++] = reg & 0xFF;
        buf[len++] = data[0]; // 高字节
        buf[len++] = data[1]; // 低字节
        break;
    case 0x10: // 写多个寄存器
        buf[len++] = reg >> 8;
        buf[len++] = reg & 0xFF;
        buf[len++] = num >> 8;
        buf[len++] = num & 0xFF;
        buf[len++] = num * 2; // 字节数
        for (uint16_t i = 0; i < num * 2; i++)
            buf[len++] = data[i];
        break;
    default:
        return; // 不支持功能码
    }

    uint16_t crc = MB_CRC16(buf, len);
    buf[len++] = crc & 0xFF;
    buf[len++] = crc >> 8;

    cm_uart_write(CM_UART_DEV_0, buf, len, 100);
    // 打印

    modbus_log_data(MB_DATA_SEND, len, buf); // 打印发送数据
}

/**
 * @brief 打印Modbus发送或接收的数据日志 (使用枚举)
 *
 * @param dir 数据方向，可以是 MB_DATA_SEND 或 MB_DATA_RECEIVE
 * @param len 数据长度
 * @param buf 数据缓冲区指针
 */
void modbus_log_data(mb_data_direction_t dir, uint16_t len, const uint8_t *buf)
{

    const char *prefix;
    if (dir == MB_DATA_SEND)
    {
        prefix = "MB_SendRequest";
    }
    else
    {
        prefix = "MB_ReceiveResponse";
    }

    // 1. 处理空数据的情况
    if (len == 0 || buf == NULL)
    {
        cm_log_printf(0, "MB_SendRequest: data=empty (len=0 or buf=NULL)\n");
        return;
    }

    // 2. 计算所需缓冲区大小
    //    每个字节格式化为 "%02X " 需要 3 个字符，最后一个字节后面不加空格
    //    所以总长度为 len * 3 (最坏情况)，再加上字符串结束符 '\0'
    size_t buf_size = (len * 3) + 1;

    // 3. 使用项目提供的 cm_malloc 分配内存
    char *log_str = (char *)cm_malloc(buf_size);
    if (log_str == NULL)
    {
        // 内存分配失败，打印错误日志
        cm_log_printf(0, "MB_SendRequest: Failed to allocate memory for log string\n");
        return;
    }

    // 4. 使用 snprintf 安全地拼接字符串
    char *current_pos = log_str;
    size_t remaining_size = buf_size;

    for (uint16_t i = 0; i < len; i++)
    {
        // snprintf 返回写入的字符数（不包括结束符）
        int written = snprintf(current_pos, remaining_size, "%02X", buf[i]);

        // 检查是否有足够的空间，防止缓冲区溢出
        if (written < 0 || written >= remaining_size)
        {
            cm_log_printf(0, "MB_SendRequest: Log string buffer overflow\n");
            break;
        }

        current_pos += written;
        remaining_size -= written;

        // 如果不是最后一个字节，添加一个空格
        if (i != len - 1)
        {
            if (remaining_size > 1)
            {
                *current_pos++ = ' ';
                remaining_size--;
            }
            else
            {
                // 空间不足，提前结束
                break;
            }
        }
    }

    // 确保字符串总是以 null 结尾
    *current_pos = '\0';

    // 5. 一次性打印拼接好的字符串
    cm_log_printf(0, "%s: data=%s\n", prefix, log_str);

    // 6. 使用 cm_free 释放之前分配的内存，避免内存泄漏
    cm_free(log_str);
    log_str = NULL; // 将指针置为 NULL 是一个好习惯
}